-module(cus_conversation).

-include("cus.hrl").
-include("cus_pb.hrl").
-include("ejabberd.hrl").
-include("logger.hrl").

-include_lib("stdlib/include/qlc.hrl").


-behaviour(gen_server).


-export([
    start/0,
    init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2,
    code_change/3]).


-export([
    dump/0,
    store_message/3,
    cancel_message/4,
    update_message/2,
    create_conversation/2,
    disband_conversation/1,
    add_member/2,
    del_member/2,
    change_name/2,
    conversation_info/1
        ]).


-define(ZC, 1).

-define(JS, 0).


store_message(Cid, FromId, Record) ->
    call(Cid, {store_message, FromId, Cid, Record}).


cancel_message(Cid, ClientMid, ServerMid, Seq) ->
    cast(Cid, {cancel_message, Cid, ClientMid, ServerMid, Seq}).


update_message(Cid, Message) ->
    cast(Cid, {update_message, Message}).


create_conversation(Cid, Info) ->
    call(Cid, {create_conversation, Info}).


disband_conversation(Cid) ->
    call(Cid, {disband_conversation, Cid}).


add_member(Cid, Uid) ->
    call(Cid, {add_member, Cid, Uid}).


del_member(Cid, Uid) ->
    call(Cid, {del_member, Cid, Uid}).


change_name(Cid, Name) ->
    call(Cid, {change_name, Cid, Name}).


conversation_info(Cid) ->
    call(Cid, {conversation_info, Cid}).


start() ->
    gen_server:start(?MODULE, [], []).


%% ====================================================================
%% Behavioural functions 
%% ====================================================================
init([]) ->
    process_flag(trap_exit, true),
    {ok, {}}.


handle_call({conversation_info, Cid}, _From, State) ->
    Info = get_conversation_info(Cid),
    {reply, {ok, Info}, State};

handle_call({store_message, FromUserID, Cid, Message}, _From, State) ->
    Info = get_conversation_info(Cid),
    ClientMid = Message#msg.mid,
    Now = cus_util:utc_timestamp_str(),
    case ets:lookup(cid_and_sid_tab, ClientMid) of
        {ClientMid, ServerMid, _Time} ->
            FinalMessage = cus_redis:get_message(ServerMid),
            Return = [repeat, FinalMessage, Info],
            {reply, {ok, Return}, State};
        _ ->
            case Info of
                undefined ->
                    {reply, {error, "conversation undefined"}, State};
                _ ->
                    case Info#cus_conversation.status of
                        ?ZC ->   %%会话状态正常
                            case check_from_user(FromUserID, Info#cus_conversation.members) of
                                true ->
                                    Seq = Info#cus_conversation.seq + 1,
                                    ServerMid = lists:concat([Cid, "_", Seq]),
                                    ets:insert(cid_and_sid_tab, {ClientMid, ServerMid, Now}),
                                    FinalMessage = Message#msg{mid = ServerMid,
                                                               msgtime = Now,
                                                               conversationinfo = cus_do:pack_proto_conversation_info(Info)},
                                    Info1 = Info#cus_conversation{seq = Seq},
                                    cus_redis:set_message(FinalMessage),
                                    cus_redis:set_conversation_info(Info1),
                                    Return = [new, FinalMessage, Info1],
                                    case FromUserID of
                                        [$s, $y, $s | _] ->
                                            skip;
                                        _ ->
                                            cus_user:update_conversation_read_seq(FromUserID, Cid, Seq)
                                    end,
                                    {reply, {ok, Return}, State};
                                _ ->
                                    {reply, {error, "user not in conversation"}, State}
                            end;
                        _ ->
                            case FromUserID of
                                [$s, $y, $s | _] ->
                                    Seq = Info#cus_conversation.seq + 1,
                                    ServerMid = lists:concat([Cid, "_", Seq]),
                                    ets:insert(cid_and_sid_tab, {ClientMid, ServerMid, Now}),
                                    FinalMessage = Message#msg{mid = ServerMid,
                                                               msgtime = Now,
                                                               conversationinfo = cus_do:pack_proto_conversation_info(Info)},
                                    Info1 = Info#cus_conversation{seq = Seq},
                                    cus_redis:set_message(FinalMessage),
                                    cus_redis:set_conversation_info(Info1),
                                    Return = [new, FinalMessage, Info1],
                                    {reply, {ok, Return}, State};
                                _ ->
                                    {reply, {error, "conversation status error"}, State}
                            end
                    end
            end
    end;

handle_call({create_conversation, Info}, _From, State) ->
    Members = Info#cus_conversation.members,
    Cid = Info#cus_conversation.cid,
    Seq = Info#cus_conversation.seq,
    [cus_user:add_conversation(Uid, Cid, Seq) || Uid <- Members],
    cus_redis:set_conversation_info(Info),
    {reply, {ok, "success"}, State};

handle_call({disband_conversation, Cid}, _From, State) ->
    Info = get_conversation_info(Cid),
    Info1 = Info#cus_conversation{status = ?JS, updateTime = cus_util:utc_str()}, %%会话被解散
    cus_redis:set_conversation_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({add_member, Cid, Uid}, _From, State) ->
    Info = get_conversation_info(Cid),
    Member = Info#cus_conversation.members,
    Seq = Info#cus_conversation.seq,
    NewMember = case ?member(Uid, Member) of
                    true ->
                        Member;
                    _ ->
                        cus_user:add_conversation(Uid, Cid, Seq),
                        [Uid | Member]
                end,
    Info1 = Info#cus_conversation{members = NewMember, updateTime = cus_util:utc_str()},
    cus_redis:set_conversation_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({del_member, Cid, Uid}, _From, State) ->
    Info = get_conversation_info(Cid),
    Member = Info#cus_conversation.members,
    Seq = Info#cus_conversation.seq,
    cus_user:del_conversation(Uid, Cid, Seq),
    Info1 = Info#cus_conversation{members = lists:delete(Uid, Member),
                                  updateTime = cus_util:utc_str()},
    cus_redis:set_conversation_info(Info1),
    {reply, {ok, "success"}, State};

handle_call({change_name, Cid, Name}, _From, State) ->
    Info = get_conversation_info(Cid),
    Info1 = Info#cus_conversation{name = Name, updateTime = cus_util:utc_str()},
    cus_redis:set_conversation_info(Info1),
    {reply, {ok, "success"}, State};

handle_call(_Request, _From, State) ->
    {reply, ok, State}.

handle_cast({update_message, Message}, State) ->
    cus_redis:set_message(Message),
    {noreply, State};

handle_cast({cancel_message, Cid, ClientMid, ServerMid, Seq}, State) ->
    Info = get_conversation_info(Cid),
    ets:delete(cid_and_sid_tab, ClientMid),
    cus_redis:del_message(ServerMid),
    CurrentSeq = Info#cus_conversation.seq,
    case Seq of
        CurrentSeq ->
            Info1 = Info#cus_conversation{seq = Seq - 1},
            cus_redis:set_conversation_info(Info1);
        _ ->
            SeqList = lists:seq(Seq + 1, CurrentSeq),
            Fun = fun(Num) ->
                          OldMid = lists:concat([Cid, "_", Num]),
                          NewMid = lists:concat([Cid, "_", Num - 1]),
                          Message = cus_redis:get_message(OldMid),
                          Message1 = Message#msg{mid = NewMid},
                          cus_redis:set_message(Message1)
                  end,
            lists:foreach(Fun, SeqList)
    end,
    {noreply, State};

handle_cast(_Msg, State) ->
    {noreply, State}.

    
handle_info(_Info, State) ->
    {noreply, State}.


terminate(_Reason, _State) ->
    ok.


code_change(_OldVsn, State, _Extra) ->
    {ok, State}.


dump() ->
    Now = cus_util:utc(),
    Keys = ets:select(cid_and_sid_tab, [{{'$1', '_', '$2'}, [{'=<', '$2', (Now div 1000) - 7200}], ['$1']}]),
    [ets:delete(cid_and_sid_tab, Key) || Key <- Keys].


check_from_user([$s, $y, $s | _], _Members) ->
    true;
check_from_user(Uid, Members) ->
    ?member(Uid, Members).


get_conversation_info([]) ->
    undefined;
get_conversation_info(Cid) ->
    case cus_redis:get_conversation_info(Cid) of
        undefined ->
            case cus_util:conversation_type(Cid) of
                ?QUANLIAO ->
                    QuanId = cus_util:get_quan_id_from_cid(Cid),
                    Select = io_lib:format("select BrandSpaceMemberUserID from BrandSpaceMemberInfo where BrandSpaceID = '~s' and BrandSpaceMemberStatus = 3", [QuanId]),
                    case cus_mysql:get_all(Select) of
                        [] ->
                            undefined;
                        Data ->
                            SelectName = io_lib:format("select BrandSpaceName from BrandSpaceInfo where BrandSpaceID = '~s'", [QuanId]),
                            Name = cus_mysql:get_one(SelectName),
                            Members = lists:foldl(fun([Item], Acc) ->
                                                          Uid = ?i2l(Item),
                                                          cus_user:add_conversation(Uid, Cid, 0),
                                                          [Uid | Acc]
                                                  end, [], Data),
                            Info = #cus_conversation{cid = Cid,
                                                     name = ?b2l(Name),
                                                     members = Members},
                            cus_redis:set_conversation_info(Info),
                            Info
                    end;
                ?DANLIAO ->
                    Members = cus_util:get_users_from_cid(Cid),
                    [cus_user:add_conversation(Uid, Cid, 0) || Uid <- Members],
                    Info = #cus_conversation{cid = Cid, members = Members},
                    cus_redis:set_conversation_info(Info),
                    Info;
                _ ->
                    undefined
            end;
        Info ->
            Info
    end.


call(ConversationID, Msg) ->
    cus_proc_manage:conversation_call(ConversationID, Msg).


cast(ConversationID, Msg) ->
    cus_proc_manage:conversation_cast(ConversationID, Msg).